import {
  Injectable,
  Inject,
  BadRequestException,
  ConflictException,
} from '@nestjs/common';

import { Schema } from 'mongoose';

import { ConfigService } from '@nestjs/config';

import { DictionaryModel, DictionaryTypeModel } from '@server/models';
import { AuthService } from '@server/auth';
import { BaseResponse } from '@server/basic-dto';

import {
  CreateOrUpdateDictionaryTypeDto,
  CreateOrUpdateDictionaryDto,
  QueryDictionariesListResponse,
  QueryDictionariesByTypeResponse,
  AllDictionaryTypeResponse,
} from './dto';
import {
  DIC_TYPE_CODE_NAME_EXIST,
  DIC_TYPE_VALUE_EXIST,
  DIC_TYPE_CODE_NOT_EXIST,
} from './error';

@Injectable()
export default class Dictionaryervice {
  constructor(
    private readonly configService: ConfigService,
    private readonly authService: AuthService,
    @Inject('DICTIONARY_MODEL')
    private readonly dictionaryModel: typeof DictionaryModel,
    @Inject('DICTIONARY_TYPE_MODEL')
    private readonly dictionaryTypeModel: typeof DictionaryTypeModel,
  ) {}

  async createType(
    createDictionaryType: CreateOrUpdateDictionaryTypeDto,
    token: string,
  ): Promise<BaseResponse> {
    const count: number = await this.dictionaryTypeModel.countDocuments({
      $or: [
        {
          code: createDictionaryType.code,
        },
        {
          name: createDictionaryType.name,
        },
      ],
    });

    if (count > 0) {
      throw new ConflictException(DIC_TYPE_CODE_NAME_EXIST);
    }

    const { id: creator } = this.authService.parse(token);
    await this.dictionaryTypeModel.create(
      Object.assign({}, createDictionaryType, { creator }),
    );

    return {
      code: 0,
      message: '',
    };
  }

  async updateType(
    id: string,
    updateDictionaryType: CreateOrUpdateDictionaryTypeDto,
  ): Promise<BaseResponse> {
    const count: number = await this.dictionaryTypeModel.countDocuments({
      $or: [
        {
          code: updateDictionaryType.code,
        },
        {
          name: updateDictionaryType.name,
        },
      ],
      _id: {
        $ne: id
      }
    });

    if (count > 0) {
      throw new ConflictException(DIC_TYPE_CODE_NAME_EXIST);
    }

    await this.dictionaryTypeModel.findByIdAndUpdate(id, updateDictionaryType);

    return {
      code: 0,
      message: '',
    };
  }

  async allTypes(): Promise<AllDictionaryTypeResponse> {
    const data: any = await this.dictionaryTypeModel.find({
      code: {
        $ne: this.configService.get('localeKey')
      }
    });

    return {
      code: 0,
      message: '',
      data,
    };
  }

  async create(
    createDictionary: CreateOrUpdateDictionaryDto,
    token: string,
  ): Promise<BaseResponse> {
    const count: number = await this.dictionaryModel.countDocuments({
      type: createDictionary.type,
      value: createDictionary.value,
    });

    if (count > 0) {
      throw new ConflictException(DIC_TYPE_VALUE_EXIST);
    }

    const { id: creator } = this.authService.parse(token);

    await this.dictionaryModel.create(
      Object.assign({}, createDictionary, { creator }),
    );

    return {
      code: 0,
      message: '',
    };
  }

  async update(
    id: string,
    updateDictionary: CreateOrUpdateDictionaryDto,
  ): Promise<BaseResponse> {
    const count: number = await this.dictionaryModel.count({
      $or: [
        {
          type: updateDictionary.type,
        },
        {
          value: updateDictionary.value,
        },
      ],
      _id: {
        $ne: id
      }
    });

    if (count > 0) {
      throw new ConflictException(DIC_TYPE_VALUE_EXIST);
    }

    await this.dictionaryModel.findByIdAndUpdate(id, updateDictionary);

    return {
      code: 0,
      message: '',
    };
  }

  async delete(ids: string): Promise<BaseResponse> {
    const idsArr: Array<string> = ids.split(',');

    for (const id of idsArr) {
      await this.dictionaryModel.findByIdAndRemove(id);
    }

    return {
      code: 0,
      message: '',
    };

    return {
      code: 0,
      message: '',
    };
  }

  async list(
    page: number = 1,
    pageSize: number = 10,
  ): Promise<QueryDictionariesListResponse> {
    const data: any = await this.dictionaryModel.paginate({
      page: parseInt(`${page}`),
      limit: parseInt(`${pageSize}`),
    });

    return {
      code: 0,
      message: '',
      data,
    };
  }

  async all(typeCode: string, locale: string): Promise<QueryDictionariesByTypeResponse> {
    const condition: { 
      type?: string;
      locale?: string;
    } = {};

    if (typeCode) {
      const type: any = await this.dictionaryTypeModel.findOne({
        code: typeCode,
      });
      if (!type) {
        throw new BadRequestException(DIC_TYPE_CODE_NOT_EXIST);
      } else {
        condition.type = type.id;
      }
    }

    if (locale) {
      condition.locale = locale;
    }

    const data: any = await this.dictionaryModel.find(condition);

    return {
      code: 0,
      message: '',
      data,
    };
  }
}
